package br.com.lumis.ims.service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import br.com.lumis.ims.model.Metrica;
import br.com.lumis.ims.model.MetricaBalanceador;
import br.com.lumis.ims.model.MetricaDisco;
import br.com.lumis.ims.model.MetricaInstancia;
import br.com.lumis.ims.model.MetricaStat;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.cloudwatch.AmazonCloudWatch;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient;
import com.amazonaws.services.cloudwatch.model.Datapoint;
import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsRequest;
import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsResult;


public class CloudWatchService {

	private static final int MAX_INTERVAL = 1440;
	private static final int DEFAULT_PERIOD = 1 * 60 * 60;
	
	public Map<MetricaInstancia, Set<Metrica>> getMetricasInstancia(AWSCredentials credentials, 
			String idInstancia, Date inicio, Date fim) {
		AmazonCloudWatch cw = new AmazonCloudWatchClient(credentials);
		
		int periodo = 0;
		if(((fim.getTime() - inicio.getTime()) / 1000 / 60) > (MAX_INTERVAL * DEFAULT_PERIOD)) {
			periodo = (int) ((fim.getTime() - inicio.getTime()) / 1000 / (MAX_INTERVAL - 1));
		} else {
			periodo = DEFAULT_PERIOD;
		}
		
		Map<MetricaInstancia, Set<Metrica>> retorno = new HashMap<MetricaInstancia, Set<Metrica>>();
		
		MetricaInstancia[] metricas = MetricaInstancia.values();
		for(int i = 0; i < metricas.length; i++) {
			MetricaInstancia metrica = metricas[i];
			GetMetricStatisticsRequest request = new GetMetricStatisticsRequest().
					withStartTime(inicio).
					withNamespace(MetricaInstancia.NAMESPACE).
					withPeriod(periodo).
					withDimensions(new Dimension().withName("InstanceId").withValue(idInstancia)).
					withMetricName(metrica.nomeMetrica()).
					withStatistics(metrica.statMetrica().nomeStat()).
					withEndTime(fim);

			GetMetricStatisticsResult resultado = cw.getMetricStatistics(request);
			
			Set<Metrica> lstMetrica = new TreeSet<Metrica>();
			Iterator<Datapoint> itData = resultado.getDatapoints().iterator();
			while(itData.hasNext()) {
				Datapoint data = itData.next();
				
				Metrica obj = new Metrica();
				obj.setTimestamp(data.getTimestamp());
				obj.setValor(getValor(metrica.statMetrica(), data));
				
				lstMetrica.add(obj);
			}
			
			retorno.put(metrica, lstMetrica);
		}
		
		return retorno;
	}
	
	public Map<MetricaDisco, Set<Metrica>> getMetricasDisco(AWSCredentials credentials, 
			String idDisco, Date inicio, Date fim) {
		AmazonCloudWatch cw = new AmazonCloudWatchClient(credentials);
		
		int periodo = 0;
		if(((fim.getTime() - inicio.getTime()) / 1000 / 60) > (MAX_INTERVAL * DEFAULT_PERIOD)) {
			periodo = (int) ((fim.getTime() - inicio.getTime()) / 1000 / (MAX_INTERVAL - 1));
		} else {
			periodo = DEFAULT_PERIOD;
		}
		
		Map<MetricaDisco, Set<Metrica>> retorno = new HashMap<MetricaDisco, Set<Metrica>>();
		
		MetricaDisco[] metricas = MetricaDisco.values();
		for(int i = 0; i < metricas.length; i++) {
			MetricaDisco metrica = metricas[i];
			GetMetricStatisticsRequest request = new GetMetricStatisticsRequest().
					withStartTime(inicio).
					withNamespace(MetricaDisco.NAMESPACE).
					withPeriod(periodo).
					withDimensions(new Dimension().withName("VolumeId").withValue(idDisco)).
					withMetricName(metrica.nomeMetrica()).
					withStatistics(metrica.statMetrica().nomeStat()).
					withEndTime(fim);

			GetMetricStatisticsResult resultado = cw.getMetricStatistics(request);
			
			Set<Metrica> lstMetrica = new TreeSet<Metrica>();
			Iterator<Datapoint> itData = resultado.getDatapoints().iterator();
			while(itData.hasNext()) {
				Datapoint data = itData.next();
				
				Metrica obj = new Metrica();
				obj.setTimestamp(data.getTimestamp());
				obj.setValor(getValor(metrica.statMetrica(), data));
				
				lstMetrica.add(obj);
			}
			
			retorno.put(metrica, lstMetrica);
		}
		
		return retorno;
	}
	
	public Map<MetricaBalanceador, Set<Metrica>> getMetricasBalanceador(AWSCredentials credentials, 
			String idBalanceador, Date inicio, Date fim) {
		AmazonCloudWatch cw = new AmazonCloudWatchClient(credentials);
		
		int periodo = 0;
		if(((fim.getTime() - inicio.getTime()) / 1000 / 60) > (MAX_INTERVAL * DEFAULT_PERIOD)) {
			periodo = (int) ((fim.getTime() - inicio.getTime()) / 1000 / (MAX_INTERVAL - 1));
		} else {
			periodo = DEFAULT_PERIOD;
		}
		
		Map<MetricaBalanceador, Set<Metrica>> retorno = new HashMap<MetricaBalanceador, Set<Metrica>>();
		
		MetricaBalanceador[] metricas = MetricaBalanceador.values();
		for(int i = 0; i < metricas.length; i++) {
			MetricaBalanceador metrica = metricas[i];
			GetMetricStatisticsRequest request = new GetMetricStatisticsRequest().
					withStartTime(inicio).
					withNamespace(MetricaBalanceador.NAMESPACE).
					withPeriod(periodo).
					withDimensions(new Dimension().withName("LoadBalancerName").withValue(idBalanceador)).
					withMetricName(metrica.nomeMetrica()).
					withStatistics(metrica.statMetrica().nomeStat()).
					withEndTime(fim);

			GetMetricStatisticsResult resultado = cw.getMetricStatistics(request);
			
			Set<Metrica> lstMetrica = new TreeSet<Metrica>();
			Iterator<Datapoint> itData = resultado.getDatapoints().iterator();
			while(itData.hasNext()) {
				Datapoint data = itData.next();
				
				Metrica obj = new Metrica();
				obj.setTimestamp(data.getTimestamp());
				obj.setValor(getValor(metrica.statMetrica(), data));
				
				lstMetrica.add(obj);
			}
			
			retorno.put(metrica, lstMetrica);
		}
		
		return retorno;
	}
	
	private BigDecimal getValor(MetricaStat stat, Datapoint data) {
		if(stat.equals(MetricaStat.AVERAGE)) {
			return new BigDecimal(data.getAverage());
		} else if(stat.equals(MetricaStat.SUM)) {
			return new BigDecimal(data.getSum());
		} else if(stat.equals(MetricaStat.MAXIMUM)) {
			return new BigDecimal(data.getMaximum());
		}
		return null;
	}

}
